package com.kasax.plugin.app.transform

import com.android.build.api.transform.*
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.utils.FileUtils
import com.kasax.plugin.app.LogUtils
import com.kasax.plugin.app.visitor.log.LogClassVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter


/**
 * 參考資料:
 * https://juejin.cn/post/7055970526539284493
 */
class CustomLogTransform extends Transform {

    @Override
    String getName() {
        return "CustomLogTransform"
    }

    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS
    }

    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }

    @Override
    boolean isIncremental() {
        return false
    }

    @Override
    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        super.transform(transformInvocation)
        LogUtils.d(getName(), "------------ ------------  transform 开始  ------------ ------------  ")
        def outputProvider = transformInvocation.outputProvider
        transformInvocation.inputs.each { TransformInput input ->
            // 遍历所有依赖性的jar或者aar里面的文件进行处理
            LogUtils.d(getName(), "handle jars")
            input.jarInputs.each { JarInput jarInput ->
                // jarInput.status: 新增/变更/删除
                LogUtils.d(getName(), "jarFile-path:${jarInput.file.absolutePath} , status: ${jarInput.status}")
                def srcJarFile = jarInput.file
                // 需要将所有输入的jar里面的文件copy到输出目录，以交给下一下transform处理
                def destFile = outputProvider.getContentLocation(jarInput.name,
                        jarInput.contentTypes,
                        jarInput.scopes,
                        Format.JAR)
                LogUtils.d(getName(), "copy src-path:${srcJarFile.absolutePath} , to dest-path: ${destFile.absolutePath}")
                FileUtils.copyFile(srcJarFile, destFile)
            }

            // 遍历源码里面的文件进行处理
            LogUtils.d(getName(), "handle direcotries")
            input.directoryInputs.each { DirectoryInput directoryInput ->
                def src = directoryInput.file
                // 需要将所有输入文件copy到输出，以交给下一下transform处理
                def dest = outputProvider.getContentLocation(directoryInput.name,
                        directoryInput.contentTypes,
                        directoryInput.scopes,
                        Format.DIRECTORY)
                processInject(src, dest)
            }

        }
        LogUtils.d(getName(), "------------ ------------  transform 結束  ------------ ------------  ")
    }

    /**
     * ASM插桩的逻辑封装到此方法
     * @param src
     * @param dest
     */
    private void processInject(File src, File dest) {
        LogUtils.d(getName(), "processInject start")
        LogUtils.d(getName(), "processInject src:${src.absolutePath} , dest: ${dest.absolutePath}")
        // 遍历src下所有的class文件
        for (File file : FileUtils.getAllFiles(src)) {
            FileInputStream fis = new FileInputStream(file)
            // ClassReader读取class文件的内容
            ClassReader cr = new ClassReader(fis)
            // ClassWriter用于回写修改后的class内容
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES)
            // ClassWriter其实也是ClassVisitor的子类，这样就可构造出一个自定义的ClassVisitor
            // ClassVisitor处理class
            cr.accept(new LogClassVisitor(cw), ClassReader.EXPAND_FRAMES)
            // 处理后的数据写回文件
            def newClsBytes = cw.toByteArray()
            // 写入目标目录
            def fullClsPath = file.absolutePath.replace(src.absolutePath, "")
            def outFile = new File(dest, fullClsPath)
            LogUtils.d(getName(), "processInject outFile:${outFile.absolutePath}")
            FileUtils.mkdirs(outFile.parentFile)
            def fos = new FileOutputStream(outFile)
            fos.write(newClsBytes)
            fos.close()
        }
        LogUtils.d(getName(), "processInject complete")
    }
}